﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.IO;

namespace WebServerX
{
    /// <remarks>
    /// See http://en.wikipedia.org/wiki/List_of_HTTP_headers for a full list of headers
    /// </remarks>
    public class Request
    {
        #region Properties

        public RequestType Type
        {
            get { return _type; }
        }
        private RequestType _type = RequestType.Get;

        public string Url
        {
            get { return _url; }
        }
        private string _url = String.Empty;

        public Uri Uri
        {
            get { return _uri; }
        }
        private Uri _uri = null;

        public HttpVersion HttpVersion
        {
            get { return _httpVersion; }
        }
        private HttpVersion _httpVersion = HttpVersion.HTTP1_1;

        public string Host
        {
            get { return _host; }
        }
        private string _host = String.Empty;

        #endregion

        #region Public Methods

        public bool Get(NetworkStream ns)
        {
            if (!ns.CanRead)
                return false;

            byte[] message = new byte[4096];
            int bytesRead = 0;

            string readMessage = String.Empty;

            while (ns.DataAvailable)
            {
                bytesRead = 0;

                try
                {
                    bytesRead = ns.Read(message, 0, 4096);
                }
                catch
                {
                    return false;
                }

                if (bytesRead == 0)
                {
                    break;
                }

                ASCIIEncoding encoder = new ASCIIEncoding();
                readMessage += encoder.GetString(message, 0, bytesRead);
            }

            if (!this.ProcessRequestHeader(readMessage))
                return false;

            return true;
        }

        #endregion

        #region Private Methods

        private bool ProcessRequestHeader(string readMessage)
        {
            List<string> lines = new List<string>();

            StringReader reader = new StringReader(readMessage);

            string line = String.Empty;
            while (!String.IsNullOrEmpty((line = reader.ReadLine())))
            {
                lines.Add(line);
            }

            if (lines.Count < 2)
                return false;

            // read the request line 1 - the header line
            if (!this.ReadHeaderLine(lines[0]))
                return false;

            for(int i = 1; i < lines.Count; i++)
            {
                this.ReadLine(lines[i]);
            }

            // we are only supporting http, not https
            this._uri = new Uri(String.Format("http://{0}{1}", this.Host, this.Url));

            return true;
        }

        private bool ReadHeaderLine(string line)
        {
            string[] parts = line.Split(' ');

            if (parts.Length != 3)
                return false;

            if (!this.SetRequestType(parts[0]))
                return false;

            this._url = parts[1];

            if (!this.SetRequestHttpType(parts[2]))
                return false;

            return true;
        }

        private bool SetRequestHttpType(string httpType)
        {
            httpType = httpType.Trim();

            switch (httpType)
            {
                case "HTTP/1.0":
                    this._httpVersion = WebServerX.HttpVersion.HTTP1_0;
                    break;
                case "HTTP/1.1":
                    this._httpVersion = WebServerX.HttpVersion.HTTP1_1;
                    break;
                default:
                    return false;
            }

            return true;
        }

        private bool SetRequestType(string part)
        {
            part = part.ToLower().Trim();

            switch (part)
            {
                case "head":
                    this._type = RequestType.Head;
                    break;
                case "get":
                    this._type = RequestType.Get;
                    break;
                case "post":
                    this._type = RequestType.Post;
                    break;
                case "put":
                    this._type = RequestType.Put;
                    break;
                case "delete":
                    this._type = RequestType.Delete;
                    break;
                case "trace":
                    this._type = RequestType.Trace;
                    break;
                case "options":
                    this._type = RequestType.Options;
                    break;
                case "connect":
                    this._type = RequestType.Connect;
                    break;
                case "patch":
                    this._type = RequestType.Patch;
                    break;
                default:
                    return false;
            }

            return true;
        }

        private void ReadLine(string line)
        {
            string key = String.Empty;
            string value = String.Empty;

            int idx = 0;
            foreach (char c in line)
            {
                if (c == ':')
                    break;

                key += c;
                idx++;
            }

            idx++; // move past the :

            value = line.Substring(idx);

            this.ReadRequestKeyValue(key, value);
        }

        private void ReadRequestKeyValue(string key, string value)
        {
            key = key.ToLower().Trim();
            value = value.Trim(); // no tolower

            switch (key)
            {
                case "host":
                    this._host = value;
                    break;
                default:
                    return; // we are no supporting all request header keys yet (05/08/2010)
            }
        }

        #endregion
    }
}
